/*
 *  VShader.cpp
 *  RTR
 *
 *  Created by Bernardo Franceschin on 4/21/11.
 *
 */

#include "vshader.h"
#include<stdio.h>
#include<stdlib.h>

static bool CompileShader (GLuint id)
{ 
  //printf("CompileShader %d\n",id);
  GLint status;
  glCompileShader(id);
  glGetShaderiv(id, GL_COMPILE_STATUS, &status);
  //printf("status %d\n",status);
  if (!status) { 
    GLint len;
    glGetShaderiv(id, GL_INFO_LOG_LENGTH, &len);
    char* message = (char*) malloc(len*sizeof(char));
    glGetShaderInfoLog(id, len, 0, message);
    printf("%s\n",message);
    free(message);
    return false;
  }
  return true;
}

static bool LinkProgram (GLuint id)
{ 
  GLint status;
  glLinkProgram(id);
  glGetProgramiv(id, GL_LINK_STATUS, &status);
  if (!status) { 
    GLint len;
    glGetProgramiv(id, GL_INFO_LOG_LENGTH, &len);
    char* message = (char*) malloc(len*sizeof(char));
    glGetProgramInfoLog(id, len, 0, message);
    printf("%s\n",message);
    free(message);
    return false;
  }
  return true;
}

static long FSize (FILE* f)
{
  fseek(f, 0L, SEEK_END);
  long sz = ftell(f);  
  fseek(f, 0, SEEK_SET);
  return sz;
}

static char * ReadFile(char * filename)
{
  FILE* f = fopen(filename,"r"); 
  long size = FSize(f);
  char * src = (char*) malloc(size+1);
  fread(src,1,size,f);
  src[size] = '\0';
  fclose(f);
  return src;
}

//n default = 1
bool VShader::SetVertSource (const char ** src, int n)
{
  v_id = glCreateShader(GL_VERTEX_SHADER);
  //printf("Vert:%d\n%s\n",v_id,src[0]);
  if (v_id==0){
    printf("Could not create vertex shader object\n");
    return false;
  }
  glShaderSource(v_id, n, src, 0);
  return true;
}

bool VShader::SetFragSource (const char ** src, int n)
{
  f_id = glCreateShader(GL_FRAGMENT_SHADER);
  //printf("Frag: %d\n%s\n",f_id,src[0]);
  if (f_id==0){
    printf("Could not create fragment shader object\n");
  }
  glShaderSource(f_id, n, src, 0);
  return true;
}

bool VShader::SetGeometrySource (const char ** src, int n)
{
  g_id = glCreateShader(GL_GEOMETRY_SHADER_EXT);
  //printf("Frag: %d\n%s\n",f_id,src[0]);
  if (g_id==0){
    printf("Could not create fragment shader object\n");
  }
  glShaderSource(g_id, n, src, 0);
  return true;
}

bool VShader::SetVertFile (char * filename)
{
  char * src = ReadFile(filename);
  SetVertSource((const char **) &src,1);
  free(src);
  return true;
}

bool VShader::SetFragFile (char * filename)
{
  char * src = ReadFile(filename);
  SetFragSource((const char **) &src,1);
  free(src);
  return true;
}

bool VShader::SetGeometryFile (char * filename)
{
  char * src = ReadFile(filename);
  SetGeometrySource((const char **) &src,1);
  free(src);
  return true;
}

bool VShader::CreateProgram ()
{ 
  if(!CompileShader(v_id))
    return false;
  if(!CompileShader(f_id))
    return false;
  if(g_id!=-1)
    if(!CompileShader(g_id))
      return false;
  p_id = glCreateProgram();
  if (p_id==0){
    printf("Could not create program object\n");
    return false;
  }
  //TODO: lista de atributos com id e nome e fazer um for 
  //      dando bind
  glBindAttribLocation(p_id,VPOS,"vPos");
  glBindAttribLocation(p_id,VNORMAL,"vNormal");
  glBindAttribLocation(p_id,VTEX,"vTexCoord");
  glBindAttribLocation(p_id,VTANGENT,"vTangent");
  glAttachShader(p_id,v_id);
  glAttachShader(p_id,f_id);
  if(g_id!=-1){
    printf("atachou\n");
    glAttachShader(p_id,g_id);
    //TODO: melhorar isso
    glProgramParameteriEXT(GetPid(),GL_GEOMETRY_INPUT_TYPE_EXT , GL_POINTS );
    glProgramParameteriEXT(GetPid(),GL_GEOMETRY_OUTPUT_TYPE_EXT , GL_TRIANGLE_STRIP);
    glProgramParameteriEXT(GetPid(),GL_GEOMETRY_VERTICES_OUT_EXT, 16);
  }
  return LinkProgram(p_id);
}


void VShader::SetAsCurrent()
{
  glUseProgram(p_id);
}

unsigned int VShader::GetUniformId(const char * name)
{
  return glGetUniformLocation(p_id, name);
}

GLuint VShader::GetPid () 
{
  return p_id;
}
